#!/bin/bash

WINESAPOS_SETUP_INTERACTIVE="${WINESAPOS_SETUP_INTERACTIVE:-true}"
WINESAPOS_IMAGE_TYPE="$(grep VARIANT_ID /usr/lib/os-release-winesapos | cut -d = -f 2)"

# The secure image requires that the "sudo" password be provided for the "winesap" user.
# This password is also required to be reset during the first login so it is unknown.
# Prompt the user to enter in their password.
# On other image types, they do not require a password to run "sudo" commands so using
# the command "sudo -S" to read the password from standard input still works as expected.
if [[ "${WINESAPOS_IMAGE_TYPE}" == "secure" ]]; then
    while true;
        do user_pw=$(kdialog --title "winesapOS First-Time Setup" --password 'Please enter your password (default: "winesap") to start the first-time setup.')
        if echo "${user_pw}" | sudo -S whoami; then
            # Break out of the "while" loop if the password works with the "sudo -S" command.
            break 2
        fi
    done
fi

# Enable shell debugging.
set -x
START_TIME=$(date --iso-8601=seconds)
exec > >(sudo tee "/var/winesapos/setup_${START_TIME}.log") 2>&1
echo "Start time: ${START_TIME}"

current_shell=$(cat /proc/$$/comm)
if [[ "${current_shell}" != "bash" ]]; then
    echo "winesapOS scripts require Bash but ${current_shell} detected. Exiting..."
    exit 1
fi

export WINESAPOS_USER_NAME="${USER}"

packages_pacman=()
pacman_install() {
    for i in "${@}"; do
        packages_pacman+=("${i}")
    done
    sudo /usr/bin/pacman --noconfirm -S --needed "$@"
}

aur_install() {
    for i in "${@}"; do
        packages_pacman+=("${i}")
    done
    yay --noconfirm -S --removemake "$@"
}

packages_flatpak=()
flatpak_install() {
    for i in "${@}"; do
        packages_flatpak+=("${i}")
    done
    sudo flatpak install -y --noninteractive "$@"
}

os_detected=$(grep -P ^ID= /etc/os-release | cut -d= -f2)

qdbus_cmd="qdbus6"

if [ "${os_detected}" != "arch" ] && [ "${os_detected}" != "manjaro" ]; then
    kdialog --title "winesapOS First-Time Setup" --msgbox "Unsupported operating system. Please use Arch Linux or Manjaro."
    exit 1
fi

if [[ "${WINESAPOS_IMAGE_TYPE}" == "secure" ]]; then
    echo "Allow passwordless 'sudo' for AUR packages installed via 'yay' to be done automatically..."
    sudo -E sh -c 'mv /etc/sudoers.d/${WINESAPOS_USER_NAME} /root/etc-sudoersd-${WINESAPOS_USER_NAME}; echo "${WINESAPOS_USER_NAME} ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/${WINESAPOS_USER_NAME}; chmod 0440 /etc/sudoers.d/${WINESAPOS_USER_NAME}'
    echo "Allow passwordless 'sudo' for AUR packages installed via 'yay' to be done automatically complete."
fi

# Enable Btrfs quotas for Snapper.
# Snapper does not work during the winesapOS build so this needs to happen during the first-time setup.
btrfs_backups_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to enable automatic file system backups? This can use a lot of storage space and slow down the operating system."; then
        backup_size_selected=$(kdialog --title "winesapOS First-Time Setup" --inputbox "Backup size limit in GiB." "50")
        export backup_size_selected
        sudo snapper -c root setup-quota
        sudo snapper -c home setup-quota
        sudo btrfs qgroup limit "${backup_size_selected}"G /.snapshots
        sudo btrfs qgroup limit "${backup_size_selected}"G /home/.snapshots
    fi
}

homebrew_install() {
    # Install dependencies.
    pacman_install base-devel procps-ng curl file git libxcrypt-compat
    NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    # shellcheck disable=SC2016
    echo 'export PATH="${PATH}:/home/linuxbrew/.linuxbrew/bin"' >> ~/.bashrc
    # shellcheck disable=SC2016
    echo 'export PATH="${PATH}:/home/linuxbrew/.linuxbrew/bin"' >> ~/.zshrc
}

nix_install() {
    curl -L https://install.determinate.systems/nix | sudo sh -s -- install --no-confirm
    sudo systemctl enable --now nix-daemon
    # shellcheck disable=SC1091
    . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
    nix-channel --add https://nixos.org/channels/nixpkgs-unstable
    nix-channel --update
}

chrome_install() {
    if ! flatpak list | grep -q com.google.Chrome; then
        flatpak_install com.google.Chrome
    fi
    if [[ ! -f /home/"${USER}"/Desktop/com.google.Chrome.desktop ]]; then
        cp /var/lib/flatpak/app/com.google.Chrome/current/active/export/share/applications/com.google.Chrome.desktop /home/"${USER}"/Desktop/
        sed -i 's/Exec=/Exec=\/usr\/bin\/eatmydata\ /g' /home/"${USER}"/Desktop/com.google.Chrome.desktop
    fi
}

decky_loader_install() {
    # First install the 'zenity' dependency.
    pacman_install zenity
    curl --location --remote-name "https://github.com/SteamDeckHomebrew/decky-installer/releases/latest/download/decky_installer.desktop" --output-dir /home/"${USER}"/Desktop/
    crudini --ini-options=nospace --set /home/"${USER}"/Desktop/decky_installer.desktop "Desktop Entry" Icon steam
}

export answer_install_ge="false"
proton_ge_install() {
    export answer_install_ge="true"
    mkdir -p /home/"${USER}"/.local/share/Steam/compatibilitytools.d/
    PROTON_GE_VERSION="GE-Proton10-25"
    curl https://github.com/GloriousEggroll/proton-ge-custom/releases/download/${PROTON_GE_VERSION}/${PROTON_GE_VERSION}.tar.gz --location --output /home/"${USER}"/.local/share/Steam/compatibilitytools.d/${PROTON_GE_VERSION}.tar.gz
    tar -x -v -f /home/"${USER}"/.local/share/Steam/compatibilitytools.d/${PROTON_GE_VERSION}.tar.gz --directory /home/"${USER}"/.local/share/Steam/compatibilitytools.d/
    rm -f /home/"${USER}"/.local/share/Steam/compatibilitytools.d/${PROTON_GE_VERSION}.tar.gz
}

zerotier_install() {
    if [[ "${WINESAPOS_IMAGE_TYPE}" == "minimal" ]]; then
        pacman_install zerotier-one
        aur_install zerotier-gui-git
    fi
    # ZeroTier GUI will fail to launch with a false-positive error if the service is not running.
    sudo systemctl enable --now zerotier-one
}

xbox_controller_install() {
    # This package contains proprietary firmware that we cannot ship
    # which is why it is installed as part of the first-time setup.
    aur_install xone-dkms-git
    sudo touch /etc/modules-load.d/winesapos-controllers.conf
    echo -e "xone-wired\nxone-dongle\nxone-gip\nxone-gip-gamepad\nxone-gip-headset\nxone-gip-chatpad\nxone-gip-guitar" | sudo tee -a /etc/modules-load.d/winesapos-controllers.conf
    for i in xone-wired xone-dongle xone-gip xone-gip-gamepad xone-gip-headset xone-gip-chatpad xone-gip-guitar;
        do sudo modprobe --verbose ${i}
    done
    sudo git clone https://github.com/forkymcforkface/xpad-noone /usr/src/xpad-noone-1.0
    # shellcheck disable=SC2010
    for kernel in $(ls -1 /usr/lib/modules/ | grep -P "^[0-9]+"); do
        sudo dkms install -m xpad-noone -v 1.0 -k "${kernel}"
    done
    echo -e "\nxpad-noone\n" | sudo tee -a /etc/modules-load.d/winesapos-controllers.conf
    echo -e "\nblacklist xpad\n" | sudo tee -a /etc/modprobe.d/winesapos-controllers.conf
    sudo rmmod xpad
    sudo modprobe xpad-noone
}

waydroid_install() {
    aur_install waydroid
    aur_install waydroid-image-gapps
}

broadcom_find_device() {
    export broadcom_network_device_found=0
    # Example output:
    # Bus 001 Device 003: ID 0a5c:bd1e Broadcom Corp. BCM43143 802.11bgn (1x1) Wireless Adapter
    if lsusb | grep -i -P "network|wireless" | grep -i -q broadcom; then
        export broadcom_network_device_found=1
    fi
    # Example output:
    # 03:00.0 Network controller: Broadcom Inc. and subsidiaries BCM4360 802.11ac Dual Band Wireless Network Adapter (rev 03)
    if lspci | grep -i -P "network|wireless" | grep -i -q broadcom; then
        export broadcom_network_device_found=1
    fi
}

# Only install Broadcom Wi-Fi drivers if (1) there is a Broadcom network adapter and (2) there is no Internet connection detected.
broadcom_wifi_auto() {
    broadcom_find_device
    if (( broadcom_network_device_found == 1 )); then
        kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Checking Internet connection..." 2 | cut -d" " -f1)
        test_internet_connection
        if [ $? -ne 1 ]; then
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog showCancelButton false
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
            kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for Broadcom proprietary Wi-Fi drivers to be installed..." 3 | cut -d" " -f1)
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
            # Blacklist drives that are known to cause conflicts with the official Broadcom 'wl' driver.
            echo -e "\nblacklist b43\nblacklist b43legacy\nblacklist bcm43xx\nblacklist bcma\nblacklist brcm80211\nblacklist brcmsmac\nblacklist brcmfmac\nblacklist brcmutil\nblacklist ndiswrapper\nblacklist ssb\nblacklist tg3\n" | sudo tee /etc/modprobe.d/winesapos-broadcom-wifi.conf
            # shellcheck disable=SC2010
            broadcom_wl_dkms_pkg=$(ls -1 /var/lib/winesapos/ | grep broadcom-wl-dkms | grep -P "zst$")
            sudo pacman -U --noconfirm /var/lib/winesapos/"${broadcom_wl_dkms_pkg}"
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 2
            echo -e "broadcom\nwl" | sudo tee -a /etc/modules-load.d/winesapos-broadcom-wifi.conf
            sudo mkinitcpio -P
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
            kdialog --title "winesapOS First-Time Setup" --msgbox "Please reboot to load new changes."
        else
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog showCancelButton false
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
        fi
    fi
}

broadcom_wifi_ask() {
    broadcom_find_device
    if (( broadcom_network_device_found == 1 )); then
        if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to install the Broadcom proprietary network driver? Try this if network connections are not working. A reboot is required when done."; then
            broadcom_wifi_auto
        fi
    fi
}

test_internet_connection() {
    # Check with https://ping.archlinux.org/ to see if we have an Internet connection.
    # shellcheck disable=SC2046 disable=SC2126
    return $(curl -s https://ping.archlinux.org/ | grep "This domain is used for connectivity checking" | wc -l)
}

loop_test_internet_connection() {
    while true;
        do kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Checking Internet connection..." 2 | cut -d" " -f1)
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog showCancelButton false
        if ! test_internet_connection; then
            "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
            # Break out of the "while" loop if we have an Internet connection.
            break 2
        fi
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
        if ! kdialog --title "winesapOS First-Time Setup" \
                --yesno "A working Internet connection for setting up graphics drivers is not detected. \
                \nPlease connect to the Internet and try again, or select Cancel to quit Setup." \
                --yes-label "Retry" \
                --no-label "Cancel"; then
            # Exit the script if the user selects "Cancel".
            exit 1
        fi
    done
}

screen_rotate_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to rotate the screen (for devices that have a tablet screen)?"; then
        rotation_selected=$(kdialog --title "winesapOS First-Time Setup" --menu "Select the desired screen orientation..." right "90 degrees right (clockwise)" left "90 degrees left (counter-clockwise)" inverted "180 degrees inverted (upside-down)")
        export fbcon_rotate=0
        if [[ "${rotation_selected}" == "right" ]]; then
            export fbcon_rotate=1
            sudo sed -i 's/GRUB_GFXMODE=.*/GRUB_GFXMODE=720x1280,auto/g' /etc/default/grub
        elif [[ "${rotation_selected}" == "left" ]]; then
            export fbcon_rotate=3
            sudo sed -i 's/GRUB_GFXMODE=.*/GRUB_GFXMODE=720x1280,auto/g' /etc/default/grub
        elif [[ "${rotation_selected}" == "inverted" ]]; then
            export fbcon_rotate=2
        fi
        # Rotate the TTY output.
        sudo -E sed -i "s/GRUB_CMDLINE_LINUX=\"/GRUB_CMDLINE_LINUX=\"fbcon:rotate=${fbcon_rotate} /g" /etc/default/grub
        echo ${fbcon_rotate} | sudo tee /sys/class/graphics/fbcon/rotate_all
        export embedded_display_port
        # Example output: eDP-1
        embedded_display_port=$(kscreen-doctor -o | grep eDP | head -n 1 | awk '{print $3}')
        # Rotate the display. This is persistent across reboots.
        kscreen-doctor "output.${embedded_display_port}.rotation.${rotation_selected}"
    fi
}

asus_setup() {
    if sudo dmidecode -s system-manufacturer | grep -P "^ASUS"; then
        echo "ASUS computer detected."
        kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for ASUS utilities to be installed..." 1 | cut -d" " -f1)
        aur_install asusctl
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    else
        echo "ASUS computer not detected."
    fi
}

framework_setup() {
    system_manufacturer=$(sudo dmidecode -s system-manufacturer)
    if [[ "${system_manufacturer}" == "Framework" ]]; then
        echo "Framework laptop detected."
        kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for Framework drivers to be installed..." 8 | cut -d" " -f1)
        if lscpu | grep -q Intel; then
            # Enable better power management of NVMe devices on Intel Framework devices.
            sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="nvme.noacpi=1 /g' /etc/default/grub
        fi
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
        # Fix keyboard.
        echo "blacklist hid_sensor_hub" | sudo tee /etc/modprobe.d/winesapos-framework-als-deactivate.conf
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 2
        # Fix firmware updates.
        sudo mkdir /etc/fwupd/
        echo -e "[uefi_capsule]\nDisableCapsuleUpdateOnDisk=true" | sudo tee /etc/fwupd/uefi_capsule.conf
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 3
        # Enable support for the ambient light sensor.
        pacman_install iio-sensor-proxy
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 4
        # Enable the ability to disable the touchpad while typing.
        sudo touch /usr/share/libinput/50-framework.quirks
        echo '[Framework Laptop 16 Keyboard Module]
MatchName=Framework Laptop 16 Keyboard Module*
MatchUdevType=keyboard
MatchDMIModalias=dmi:*svnFramework:pnLaptop16*
AttrKeyboardIntegration=internal' | sudo tee /usr/share/libinput/50-framework.quirks
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 5
        # Enable a better audio profile for Framework Laptops.
        # https://github.com/cab404/framework-dsp
        pacman_install easyeffects
        TMP=$(mktemp -d) && \
        CFG=${XDG_CONFIG_HOME:-~/.config}/easyeffects && \
        mkdir -p "${CFG}" && \
        curl -Lo "${TMP}/fwdsp.zip https://github.com/cab404/framework-dsp/archive/refs/heads/master.zip" && \
        unzip -d "${TMP}" "$TMP"/fwdsp.zip 'framework-dsp-master/config/*/*' && \
        sed -i 's|%CFG%|'"$CFG"'|g' "${TMP}"/framework-dsp-master/config/*/*.json && \
        cp -rv "${TMP}"/framework-dsp-master/config/* "${CFG}" && \
        rm -rf "${TMP}"
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 6
        # Automatically configure the correct region for the Wi-Fi device.
        COUNTRY_CODE="$(curl -s ipinfo.io | jq -r .country)"
        export COUNTRY_CODE
        ## Temporarily.
        sudo -E iw reg set "${COUNTRY_CODE}"
        ## Permanently.
        pacman_install wireless-regdb
        # shellcheck disable=SC2027 disable=SC2086
        echo "WIRELESS_REGDOM=\""${COUNTRY_CODE}"\"" | sudo tee -a /etc/conf.d/wireless-regdom
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 7
        # Enable support for the LED matrix on the Framework Laptop 16.
        aur_install inputmodule-control
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    else
        echo "Framework laptop not detected."
    fi
}

mac_setup() {
    echo "Turning on the Mac fan service if the hardware is Apple..."
    if sudo dmidecode -s system-product-name | grep -P ^Mac; then
        echo "Mac hardware detected."
        sudo systemctl enable --now mbpfan
        # Networking over USB does not work on T2 Macs.
        # https://wiki.t2linux.org/guides/postinstall/
        echo -e "blacklist cdc_ncm\nblacklist cdc_mbim\n" | sudo tee -a /etc/modprobe.d/winesapos-mac.conf
        # Enable audio workaround for T2 Macs.
        sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="efi=noruntime intel_iommu=on iommu=pt pcie_ports=compat amdgpu.dpm=0 /g' /etc/default/grub
    else
        echo "No Mac hardware detected."
    fi
    echo "Turning on the Mac fan service if the hardware is Apple complete."
}

msi_setup() {
    if sudo dmidecode -s system-manufacturer | grep -P "^Micro-Star International"; then
        if sudo dmidecode -s system-product-name | grep -P "^Claw"; then
            sudo touch /usr/lib/systemd/system-sleep/msi-claw-sleep-fix.sh
            # shellcheck disable=SC2016
            echo '#!/bin/bash

case ""$1"" in
  pre)
    /usr/bin/modprobe -r iwlmvm
    /usr/bin/modprobe -r iwlwifi
    ;;
  post)
    /usr/bin/modprobe iwlwifi
    /usr/bin/modprobe iwlmvm
    ;;
esac' | sudo tee /usr/lib/systemd/system-sleep/msi-claw-sleep-fix.sh
        sudo chmod +x /usr/lib/systemd/system-sleep/msi-claw-sleep-fix.sh
        fi
    else
        echo "MSI computer not detected."
    fi
}


steam_deck_setup() {
    if sudo dmidecode -s system-product-name | grep -P "^(Galileo|Jupiter)"; then
        # Configure S3 deep sleep.
        sudo sed -i 's/GRUB_CMDLINE_LINUX="/GRUB_CMDLINE_LINUX="mem_sleep_default=deep /g' /etc/default/grub
    fi
}

surface_setup() {
    # https://github.com/linux-surface/linux-surface/wiki/Installation-and-Setup#arch
    system_family=$(sudo dmidecode -s system-family)
    if [[ "${system_family}" == "Surface" ]]; then
        echo "Microsoft Surface laptop detected."
        kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for Microsoft Surface drivers to be installed..." 3 | cut -d" " -f1)
        # The recommended GPG key is no longer valid.
        # shellcheck disable=SC2016
        echo -e "\n[linux-surface]\nServer = https://pkg.surfacelinux.com/arch/\nSigLevel = Never" | sudo tee -a /etc/pacman.conf
        sudo pacman -S -y
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
        pacman_install iptsd
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 2
        sudo pacman -R -n --nodeps --nodeps --noconfirm libwacom
        # Install build dependencies for 'libwacom-surface' first.
        pacman_install meson ninja
        aur_install libwacom-surface
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    else
        echo "Microsoft Surface laptop not detected."
    fi
}

winesapos_version_check() {
    winesapos_ver_latest="$(curl https://raw.githubusercontent.com/winesapOS/winesapOS/stable/rootfs/usr/lib/os-release-winesapos | grep VERSION_ID | cut -d = -f 2)"
    winesapos_ver_current="$(grep VERSION_ID /usr/lib/os-release-winesapos | cut -d = -f 2)"
    # 'sort -V' does not work with semantic numbers.
    # As a workaround, adding an underline to versions without a suffix allows the semantic sort to work.
    # shellcheck disable=SC2086
    if [[ $(echo -e "${winesapos_ver_latest}\n${winesapos_ver_current}" | sed '/-/!{s/$/_/}' | sort -V) == "$(echo -e ${winesapos_ver_latest}"\n"${winesapos_ver_current} | sed '/-/!{s/$/_/}')" ]]; then
        echo "No newer version found."
        if ! kdialog --title "winesapOS First-Time Setup" --yesno "It is recommended to run the winesapOS Upgrade on the desktop first. Do you want to continue the first-time setup?"; then
            exit 0
        fi
    else
        if ! kdialog --title "winesapOS First-Time Setup" --yesno "This is an older version of winesapOS. It is recommended to either download the latest image or run the winesapOS Upgrade on the desktop first. Do you want to continue the first-time setup?"; then
            exit 0
        fi
    fi
}

# Download the Steam bootstrap files in the background.
# This allows the Steam Gamescope Session to work on the next reboot.
steam_bootstrap() {
    if [[ -f /usr/bin/steam ]]; then
        tmux new-session -d -s steam 'xwfb-run --error-file /tmp/weston.log steam &> /tmp/steam.log'
    fi
}

# Only automatically handle the case for the Steam Deck.
screen_rotate_auto() {
    # "Jupiter" is the code name for the Steam Deck LCD.
    # "Galileo" is the code name for the Steam Deck OLED.
    if sudo dmidecode -s system-product-name | grep -P "^(Galileo|Jupiter)"; then
        echo "Steam Deck hardware detected."
        export embedded_display_port
        # Example output: eDP-1
        embedded_display_port=$(kscreen-doctor -o | grep eDP | head -n 1 | awk '{print $3}')
        # Rotate the display. This is persistent across reboots.
        kscreen-doctor "output.${embedded_display_port}.rotation.right"
        # Rotate GRUB.
        sudo sed -i 's/GRUB_GFXMODE=.*/GRUB_GFXMODE=720x1280,auto/g' /etc/default/grub
        # Rotate the initramfs output.
        sudo sed -i 's/GRUB_CMDLINE_LINUX="/GRUB_CMDLINE_LINUX="fbcon:rotate=1 /g' /etc/default/grub
    fi
}

repo_mirrors_region_auto() {
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the setup to update the Pacman cache..." 2 | cut -d" " -f1)
    if [ "${os_detected}" = "arch" ]; then
        # shellcheck disable=SC2016
        echo 'Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch' | sudo tee /etc/pacman.d/mirrorlist
        # shellcheck disable=SC2016
        echo 'Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch' | sudo tee -a /etc/pacman.d/mirrorlist
    elif [[ "${os_detected}" == "manjaro" ]]; then
        sudo pacman-mirrors --geoip -f 5
    fi
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
    sudo pacman -S -y
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

repo_mirrors_region_ask() {
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the setup to find package repository mirrors..." 1 | cut -d" " -f1)
    # Dialog to ask the user what mirror region they want to use
    if [ "${os_detected}" = "arch" ]; then
        # Fetch the list of regions from the Arch Linux mirror status JSON API.
        # Some regions contain a space. We need to map each newline into an array here.
        mapfile -t mirror_regions < <(curl -s https://archlinux.org/mirrors/status/json/ | jq -r '.urls[].country' | sort | uniq | sed '1d')
    elif [ "${os_detected}" = "manjaro" ]; then
        # Fetch the list of regions from the Manjaro mirror status JSON API.
        # Unlike Arch Linux, Manjaro uses underscores instead of spaces so the logic is cleaner.
        # shellcheck disable=SC2207
        mirror_regions=( $(curl -s https://repo.manjaro.org/status.json | jq -r '.[].country' | sort | uniq) )
    fi
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close

    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the setup to update the Pacman cache..." 2 | cut -d" " -f1)
    chosen_region=$(kdialog --title "winesapOS First-Time Setup" \
                            --combobox "Select your desired mirror region, \nor press Cancel to use default settings:" \
                            "${mirror_regions[@]}")

    if [ "${os_detected}" = "arch" ]; then
        # Check if the user selected a mirror region.
        if [ -n "${chosen_region}" ]; then
            # This seems like a better idea than writing global config we cannot reliably remove a line.
            sudo reflector --verbose --latest 10 --sort rate --threads 10 --save /etc/pacman.d/mirrorlist --country "${chosen_region}"
            # Ideally we should be sorting by `rate` for consistency but it may get too slow.
        else
            # Fallback to the Arch Linux and Rackspace global mirrors.
            # shellcheck disable=SC2016
            echo 'Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch' | sudo tee /etc/pacman.d/mirrorlist
            # shellcheck disable=SC2016
            echo 'Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch' | sudo tee -a /etc/pacman.d/mirrorlist
        fi
    elif [[ "${os_detected}" == "manjaro" ]]; then
        if [ -n "${chosen_region}" ]; then
            sudo pacman-mirrors -c "${chosen_region}"
        else
            sudo pacman-mirrors -f 5
        fi
    fi
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
    sudo pacman -S -y
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

graphics_drivers_auto() {
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the graphics driver to be installed..." 1 | cut -d" " -f1)
    echo mesa | sudo tee /var/winesapos/graphics

    # Enable GSP firmware support for older NVIDIA graphics cards.
    sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="nouveau.config=NvGspRm=1 /g' /etc/default/grub

    # Here are all of the possible virtualization technologies that systemd can detect:
    # https://www.freedesktop.org/software/systemd/man/latest/systemd-detect-virt.html
    virtualization_detected="$(systemd-detect-virt)"
    # Oracle VM VirtualBox.
    if [[ "${virtualization_detected}" == "oracle" ]]; then
        sudo pacman -S --noconfirm virtualbox-guest-utils
        sudo systemctl enable --now vboxservice
        sudo usermod -a -G vboxsf winesap
    elif [[ "${virtualization_detected}" == "vmware" ]]; then
        sudo pacman -S --noconfirm \
          open-vm-tools \
          xf86-video-vmware \
          xf86-input-vmmouse \
          gtkmm3
        sudo systemctl enable --now \
          vmtoolsd \
          vmware-vmblock-fuse
    fi
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

graphics_drivers_ask() {
    graphics_selected=$(kdialog --title "winesapOS First-Time Setup" --menu "Select your desired graphics driver..." amd AMD intel Intel nvidia-open "NVIDIA Open (for DLSS, Turing and newer)" nvidia-mesa "NVIDIA Mesa (for portability, Kepler and newer)" virtualbox VirtualBox vmware VMware)
    # Keep track of the selected graphics drivers for upgrade purposes.
    echo "${graphics_selected}" | sudo tee /var/winesapos/graphics
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the graphics driver to be installed..." 2 | cut -d" " -f1)
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1

    if [[ "${graphics_selected}" == "amd" ]]; then
        true
    elif [[ "${graphics_selected}" == "intel" ]]; then
        sudo pacman -S --noconfirm \
          extra/intel-media-driver \
          extra/intel-compute-runtime
    elif [[ "${graphics_selected}" == "nvidia-open" ]]; then
        sudo pacman -S --noconfirm \
          extra/nvidia-open-dkms \
          extra/nvidia-utils \
          multilib/lib32-nvidia-utils \
          extra/opencl-nvidia \
          multilib/lib32-opencl-nvidia

        # Enable Wayland support.
        sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="nvidia_drm.modeset=1 nvidia_drm.fbdev=1 /g' /etc/default/grub

        # Block the loading of conflicting open source NVIDIA drivers.
        sudo touch /etc/modprobe.d/winesapos-nvidia.conf
        echo "blacklist nova
blacklist nouveau
blacklist nvidiafb
blacklist nv
blacklist rivafb
blacklist rivatv
blacklist uvcvideo" | sudo tee /etc/modprobe.d/winesapos-nvidia.conf

        # Enable NVIDIA services to prevent crashes.
        # https://github.com/winesapOS/winesapOS/issues/837
        sudo systemctl enable nvidia-hibernate nvidia-persistenced nvidia-powerd nvidia-resume nvidia-suspend

        # Enable NVIDIA hibernation support.
        sudo mkdir /var/tmp-nvidia
        echo "options nvidia NVreg_PreserveVideoMemoryAllocations=1
options nvidia NVreg_TemporaryFilePath=/var/tmp-nvidia" | sudo tee /etc/modprobe.d/winesapos-nvidia-hibernate.conf
    elif [[ "${graphics_selected}" == "nvidia-mesa" ]]; then
        # Enable GSP firmware support for older graphics cards.
        sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="/GRUB_CMDLINE_LINUX_DEFAULT="nouveau.config=NvGspRm=1 /g' /etc/default/grub

        # Enable experimental support for old NVIDIA graphics cards starting with Kepler.
        echo "NVK_I_WANT_A_BROKEN_VULKAN_DRIVER=1" | sudo tee -a /etc/environment

        # Block the loading of conflicting NVIDIA Open Kernel Module drivers.
        sudo touch /etc/modprobe.d/winesapos-nvidia.conf
        echo "blacklist nvidia
blacklist nvidiafb
blacklist nvidia_drm
blacklist i2c_nvidia_gpu" | sudo tee /etc/modprobe.d/winesapos-nvidia.conf
    elif [[ "${graphics_selected}" == "virtualbox" ]]; then
        sudo pacman -S --noconfirm virtualbox-guest-utils
        sudo systemctl enable --now vboxservice
        sudo usermod -a -G vboxsf winesap
    elif [[ "${graphics_selected}" == "vmware" ]]; then
        sudo pacman -S --noconfirm \
          open-vm-tools \
          xf86-video-vmware \
          xf86-input-vmmouse \
          gtkmm3
        sudo systemctl enable --now \
          vmtoolsd \
          vmware-vmblock-fuse
    fi
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

swap_method_auto() {
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for zram to be enabled..." 1 | cut -d" " -f1)
    # Configure optimized zram settings based on our own research and testing.
    echo "vm.swappiness = 180
vm.watermark_boost_factor = 0
vm.watermark_scale_factor = 125
vm.page-cluster = 1" | sudo tee /etc/sysctl.d/99-vm-zram-parameters.conf
    echo "[zram0]
zram-size = ram * 2
compression-algorithm = lz4" | sudo tee /etc/systemd/zram-generator.conf
    sudo systemctl daemon-reload && sudo systemctl enable systemd-zram-setup@zram0.service
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

swap_method_ask() {
    swap_selected=$(kdialog --title "winesapOS First-Time Setup" --menu "Select your method for swap..." zram "zram (fast to create, does not enable hibernation, uses CPU)" swapfile "swapfile (slow to create, enables hibernation, uses I/O)" none "none")
    if [[ "${swap_selected}" == "zram" ]]; then
        swap_method_auto
    elif [[ "${swap_selected}" == "swapfile" ]]; then
        kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the swapfile to be enabled..." 1 | cut -d" " -f1)
        # shellcheck disable=SC1083 disable=SC2003 disable=SC2046
        swap_size_suggested="$(expr $(grep MemTotal /proc/meminfo  | awk {'print $2'}) / 1024 / 1024 + 1)"
        swap_size_selected=$(kdialog --title "winesapOS First-Time Setup" --inputbox "Swap size in GB. Set to RAM size or more for hibernation support." "${swap_size_suggested}")
        if echo "${swap_size_selected}" | grep -q -P "^[1-9]"; then
            echo "vm.swappiness=1" | sudo tee -a /etc/sysctl.d/00-winesapos.conf
            sudo touch /swap/swapfile
            # Avoid Btrfs copy-on-write.
            sudo chattr +C /swap/swapfile
            # Now fill in the swap file.
            sudo dd if=/dev/zero of=/swap/swapfile bs=1M count="${swap_size_selected}000"
            # A swap file requires strict permissions to work.
            sudo chmod 0600 /swap/swapfile
            sudo mkswap /swap/swapfile
            sudo swaplabel --label winesapos-swap /swap/swapfile
            sudo swapon /swap/swapfile
            echo "/swap/swapfile    none    swap    defaults    0 0" | sudo tee -a /etc/fstab
            # Enable hibernation support.
            sudo sed -i 's/fsck/resume fsck/g' /etc/mkinitcpio.conf
        fi
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    fi
}

time_auto() {
    sudo touch /etc/localtime
    sudo tzupdate
}

locale_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to change the current locale (en_US.UTF-8 UTF-8)?"; then
        if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to see all availables locales in /etc/locale.gen?"; then
            kdialog --title "winesapOS First-Time Setup" --textbox /etc/locale.gen
        fi
        locale_selected=$(kdialog --title "winesapOS First-Time Setup" --inputbox "Locale for /etc/locale.gen:" "en_US.UTF-8 UTF-8")
        kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the locale to be setup..." 2 | cut -d" " -f1)
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
        echo "${locale_selected}" | sudo tee -a /etc/locale.gen
        sudo locale-gen
        sudo sed -i '/^LANG/d' /etc/locale.conf
        echo "LANG=$(echo "${locale_selected}" | cut -d' ' -f1)" | sudo tee -a /etc/locale.conf
        sed -i '/^LANG/d' /home/"${USER}"/.config/plasma-localerc
        echo "LANG=$(echo "${locale_selected}" | cut -d' ' -f1)" >> /home/"${USER}"/.config/plasma-localerc
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    fi
}

time_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to change the current time zone (UTC)?"; then
        selected_time_zone=$(kdialog --title "winesapOS First-Time Setup" --combobox "Select the desired time zone:" "$(timedatectl list-timezones)")
        sudo timedatectl set-timezone "${selected_time_zone}"
    fi
}

desktops_ask() {
    desktops=$(kdialog --title "winesapOS First-Time Setup" --separate-output --checklist "Optionally select additional desktop environments to install:" \
        cosmic "COSMIC (traditional)" off \
        gnome "GNOME (traditional)" off \
        hyprland "Hyprland (tiling)" off \
        sway "Sway (tiling)" off)
    for desktop in ${desktops}
        do kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for ${prodpkg} to be installed..." 2 | cut -d" " -f1)
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1

        if echo "${desktop}" | grep cosmic; then
            pacman_install cosmic-session cosmic-files cosmic-terminal cosmic-text-editor cosmic-wallpapers
        elif echo "${desktop}" | grep gnome; then
            pacman_install gnome gnome-tweaks
            if [[ "${os_detected}" == "manjaro" ]]; then
                pacman_install manjaro-gnome-settings manjaro-settings-manager
            fi
        elif echo "${desktop}" | grep hyprland; then
            pacman_install hyprland kitty wofi
        elif echo "${desktop}" | grep sway; then
            pacman_install dmenu foot sway swaylock swayidle swaybg wmenu
        fi

        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    done
}

productivity_auto() {
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for recommended productivity applications to be installed..." 19 | cut -d" " -f1)
    # Calibre for an ebook manager.
    flatpak_install com.calibre_ebook.calibre
    cp /var/lib/flatpak/app/com.calibre_ebook.calibre/current/active/export/share/applications/com.calibre_ebook.calibre.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
    # Cheese for a webcam utility.
    flatpak_install org.gnome.Cheese
    cp /var/lib/flatpak/app/org.gnome.Cheese/current/active/export/share/applications/org.gnome.Cheese.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 2
    # ClamAV / ClamTk anti-virus.
    pacman_install clamav clamtk
    sudo freshclam
    cp /usr/share/applications/clamtk.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 3
    # CoolerControl for computer fan management.
    aur_install coolercontrol
    cp /usr/share/applications/org.coolercontrol.CoolerControl.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 4
    # FileZilla for FTP file transfers.
    flatpak_install org.filezillaproject.Filezilla
    cp /var/lib/flatpak/exports/share/applications/org.filezillaproject.Filezilla.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 5
    # Flatseal for managing Flatpaks.
    flatpak_install com.github.tchx84.Flatseal
    cp /var/lib/flatpak/app/com.github.tchx84.Flatseal/current/active/export/share/applications/com.github.tchx84.Flatseal.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 6
    # GIMP photo editor.
    flatpak_install org.gimp.GIMP
    cp /var/lib/flatpak/app/org.gimp.GIMP/current/active/export/share/applications/org.gimp.GIMP.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 7
    # Google Chrome web browser.
    chrome_install
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 8
    # GParted for managing partitions.
    # Homebrew package manager.
    homebrew_install
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 9
    # Kalk for a calculator.
    flatpak_install org.kde.kalk
    cp /var/lib/flatpak/app/org.kde.kalk/current/active/export/share/applications/org.kde.kalk.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 10
    # KeePassXC for an encrypted password manager.
    flatpak_install org.keepassxc.KeePassXC
    cp /var/lib/flatpak/app/org.keepassxc.KeePassXC/current/active/export/share/applications/org.keepassxc.KeePassXC.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 11
    # LibreOffice for an office suite.
    flatpak_install org.libreoffice.LibreOffice
    cp /var/lib/flatpak/app/org.libreoffice.LibreOffice/current/active/export/share/applications/org.libreoffice.LibreOffice.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 12
    # mpv for HDR video playback.
    pacman_install mpv
    cp /usr/share/applications/mpv.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 13
    # Nix package manager.
    nix_install
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 14
    # PeaZip compression utility.
    flatpak_install io.github.peazip.PeaZip
    cp /var/lib/flatpak/app/io.github.peazip.PeaZip/current/active/export/share/applications/io.github.peazip.PeaZip.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 15
    # qBittorrent for torrents.
    flatpak_install org.qbittorrent.qBittorrent
    cp /var/lib/flatpak/app/org.qbittorrent.qBittorrent/current/active/export/share/applications/org.qbittorrent.qBittorrent.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 16
    # QDirStat for managing storage space.
    aur_install qdirstat
    cp /usr/share/applications/qdirstat.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 17
    # VeraCrypt for managing encrypted storage.
    pacman_install veracrypt
    cp /usr/share/applications/veracrypt.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 18
    # VLC media player.
    flatpak_install org.videolan.VLC
    cp /var/lib/flatpak/app/org.videolan.VLC/current/active/export/share/applications/org.videolan.VLC.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

productivity_ask() {
    prodpkgs=$(kdialog --title "winesapOS First-Time Setup" --separate-output --checklist "Select productivity packages to install:" \
                       com.calibre_ebook.calibre:flatpak "Calibre (ebooks)" off \
                       org.gnome.Cheese:flatpak "Cheese (webcam)" off \
                       com.gitlab.davem.ClamTk:flatpak "ClamTk (anti-virus)" off \
                       coolercontrol:pkg "CoolerControl (fan control)" off \
                       org.filezillaproject.Filezilla:flatpak "FileZilla (FTP)" off \
                       com.github.tchx84.Flatseal:flatpak "Flatseal (Flatpak manager)" off \
                       org.gimp.GIMP:flatpak "GIMP (photo editing)" off \
                       com.google.Chrome "Google Chrome (web browser)" off \
                       homebrew:other "Homebrew (package manager)" off \
                       org.kde.kalk:flatpak "Kalk (calculator)" off \
                       org.keepassxc.KeePassXC:flatpak "KeePassXC (password manager)" off \
                       org.libreoffice.LibreOffice:flatpak "LibreOffice (office suite)" off \
                       mpv:pkg "mpv (HDR video playback)" off \
                       nix:other "Nix (package manager)" off \
                       io.github.peazip.PeaZip:flatpak "PeaZip (compression)" off \
                       qdirstat:pkg "QDirStat (storage space analyzer)" off \
                       org.qbittorrent.qBittorrent:flatpak "qBittorrent (torrent)" off \
                       veracrypt:pkg "VeraCrypt (file encryption)" off \
                       org.videolan.VLC:flatpak "VLC (media player)" off)
    for prodpkg in ${prodpkgs}
        do kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for ${prodpkg} to be installed..." 2 | cut -d" " -f1)
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1

        if echo "${prodpkg}" | grep -P ":flatpak$"; then
            flatpak_install "$(echo "${prodpkg}" | cut -d: -f1)"
        fi

        if echo "${prodpkg}" | grep -P ":pkg$"; then
            aur_install "$(echo "${prodpkg}" | cut -d: -f1)"
        fi

        if echo "${gamepkg}" | grep -P "^homebrew:other$"; then
            homebrew_install
        fi

        if echo "${gamepkg}" | grep -P "^nix:other$"; then
            nix_install
        fi

        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    done
}

gaming_auto() {
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for recommended gaming applications to be installed..." 32 | cut -d" " -f1)
    # AntiMicroX for configuring controller input.
    flatpak_install io.github.antimicrox.antimicrox
    cp /var/lib/flatpak/app/io.github.antimicrox.antimicrox/current/active/export/share/applications/io.github.antimicrox.antimicrox.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
    # Bottles for running any Windows game or application.
    flatpak_install com.usebottles.bottles
    cp /var/lib/flatpak/app/com.usebottles.bottles/current/active/export/share/applications/com.usebottles.bottles.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 2
    # Chiaki for PS4 and PS5 game streaming.
    flatpak_install io.github.streetpea.Chiaki4deck
    cp /var/lib/flatpak/app/io.github.streetpea.Chiaki4deck/current/active/export/share/applications/io.github.streetpea.Chiaki4deck.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 3
    # ckb-next.
    aur_install ckb-next
    cp /usr/share/applications/ckb-next.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 4
    # Decky Loader.
    decky_loader_install
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 5
    # Discord for social gaming.
    flatpak_install com.discordapp.Discord
    cp /var/lib/flatpak/app/com.discordapp.Discord/current/active/export/share/applications/com.discordapp.Discord.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 6
    # EmuDeck.
    EMUDECK_URL=$(curl -s https://api.github.com/repos/EmuDeck/emudeck-electron/releases/latest \
  | jq -r '.assets[] | select(.name | endswith(".AppImage")) | .browser_download_url')
    curl --location "${EMUDECK_URL}" --output /home/"${USER}"/Desktop/EmuDeck.AppImage
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 7
    # MangoHud.
    # Install this before GOverlay to make sure we have the right dependencies installed.
    # Otherwise, it installs the non-git version of the packages.
    aur_install mangohud-git lib32-mangohud-git
    # GOverlay.
    aur_install goverlay-git
    cp /usr/share/applications/io.github.benjamimgois.goverlay.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 8
    # Heroic Games Launcher.
    flatpak_install com.heroicgameslauncher.hgl
    cp /var/lib/flatpak/app/com.heroicgameslauncher.hgl/current/active/export/share/applications/com.heroicgameslauncher.hgl.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 9
    # LACT for overclocking and/or undervolting the GPU.
    flatpak_install io.github.ilya_zlobintsev.LACT
    cp /var/lib/flatpak/app/io.github.ilya_zlobintsev.LACT/current/active/export/share/applications/io.github.ilya_zlobintsev.LACT.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 10
    # Ludusavi.
    aur_install ludusavi
    cp /usr/share/applications/com.mtkennerly.ludusavi.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 11
    # Lutris.
    flatpak_install net.lutris.Lutris
    cp /var/lib/flatpak/app/net.lutris.Lutris/current/active/export/share/applications/net.lutris.Lutris.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 12
    # Flatpak's non-interactive mode does not work for MangoHud.
    # Instead, install a specific version of MangoHud.
    # https://github.com/winesapOS/winesapOS/issues/336
    flatpak_install runtime/org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/23.08
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 13
    # Moonlight and Sunshine.
    flatpak_install com.moonlight_stream.Moonlight dev.lizardbyte.app.Sunshine
    cp /var/lib/flatpak/app/com.moonlight_stream.Moonlight/current/active/export/share/applications/com.moonlight_stream.Moonlight.desktop /home/"${USER}"/Desktop/
    cp /var/lib/flatpak/app/dev.lizardbyte.app.Sunshine/current/active/export/share/applications/dev.lizardbyte.app.Sunshine.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 14
    # Nexus Mods app.
    aur_install nexusmods-app-bin
    cp /usr/share/applications/com.nexusmods.app.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 15
    # NonSteamLaunchers.
    curl --location --remote-name "https://raw.githubusercontent.com/moraroy/NonSteamLaunchers-On-Steam-Deck/refs/heads/main/NonSteamLaunchers.desktop" --output-dir /home/"${USER}"/Desktop/
    # NVIDIA GeForce Now.
    ## A dependency for NVIDIA GeForce Now and Xbox Cloud Gaming is Google Chrome.
    chrome_install
    ln -s /home/"${USER}"/.winesapos/winesapos-ngfn.desktop /home/"${USER}"/Desktop/winesapos-ngfn.desktop
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 16
    # Oversteer for managing racing wheels.
    flatpak_install io.github.berarma.Oversteer
    cp /var/lib/flatpak/app/io.github.berarma.Oversteer/current/active/export/share/applications/io.github.berarma.Oversteer.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 17
    # Playtron GameLAB.
    curl --location "https://api.playtron.one/api/v1/gamelab/download/linux_x64_appimage/latest" --output /home/"${USER}"/Desktop/GameLAB.AppImage
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 18
    # Polychromatic.
    aur_install polychromatic
    cp /usr/share/applications/polychromatic.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 19
    # Prism Launcher for playing Minecraft.
    flatpak_install org.prismlauncher.PrismLauncher
    cp /var/lib/flatpak/app/org.prismlauncher.PrismLauncher/current/active/export/share/applications/org.prismlauncher.PrismLauncher.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 20
    # Proton-GE.
    proton_ge_install
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 21
    # Protontricks for managing dependencies in Proton.
    flatpak_install com.github.Matoking.protontricks
    ## Add a wrapper script so that the Flatpak can be used normally via the CLI.
    echo '#!/bin/bash
flatpak run com.github.Matoking.protontricks $@
' | sudo tee /usr/local/bin/protontricks
    sudo chmod +x /usr/local/bin/protontricks
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 22
    # ProtonUp-Qt for managing GE-Proton versions.
    flatpak_install net.davidotek.pupgui2
    cp /var/lib/flatpak/app/net.davidotek.pupgui2/current/active/export/share/applications/net.davidotek.pupgui2.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 23
    # OBS Studio for screen recording and live streaming.
    flatpak_install com.obsproject.Studio
    cp /var/lib/flatpak/app/com.obsproject.Studio/current/active/export/share/applications/com.obsproject.Studio.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 24
    # Open Gamepad UI.
    aur_install opengamepadui-bin opengamepadui-session-git
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 25
    # RemotePlayWhatever.
    aur_install remoteplaywhatever
    # RetroDECK.
    flatpak_install net.retrodeck.retrodeck
    cp /var/lib/flatpak/app/net.retrodeck.retrodeck/current/active/export/share/applications/net.retrodeck.retrodeck.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 26
    # Steam.
    pacman_install steam steam-native-runtime
    cp /usr/share/applications/steam.desktop /home/"${USER}"/Desktop/
    steam_bootstrap
    aur_install gamescope-session-git gamescope-session-steam-git
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 27
    # Steam Tinker Launch.
    aur_install steamtinkerlaunch-git
    cp /usr/share/applications/steamtinkerlaunch.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 28
    # umu-launcher.
    aur_install umu-launcher
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 29
    # Waydroid.
    waydroid_install
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 30
    # Xbox Cloud Gaming.
    ln -s /home/"${USER}"/.winesapos/winesapos-xcloud.desktop /home/"${USER}"/Desktop/winesapos-xcloud.desktop
    # Xbox controller drivers.
    xbox_controller_install
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 31
    # ZeroTier.
    zerotier_install
    cp /usr/share/applications/zerotier-gui.desktop /home/"${USER}"/Desktop/
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

gaming_ask() {
    gamepkgs=$(kdialog --title "winesapOS First-Time Setup" --separate-output --checklist "Select gaming packages to install:" \
                 io.github.antimicrox.antimicrox:flatpak "AntiMicroX" off \
                 com.usebottles.bottles:flatpak "Bottles" off \
                 io.github.streetpea.Chiaki4deck:flatpak "Chiaki (PS4 and PS5 game streaming client)" off \
                 ckb-next:pkg "ckb-next (Corsair RGB)" off \
                 deckyloader:other "Decky Loader" off \
                 com.discordapp.Discord:flatpak "Discord" off \
                 emudeck:other "EmuDeck" off \
                 gamemode:pkg "GameMode (64-bit)" off \
                 lib32-gamemode:pkg "GameMode (32-bit)" off \
                 gamescope:other "Gamescope Session" off \
                 game-devices-udev:pkg "games-devices-udev (extra controller support)" off \
                 goverlay-git:pkg "GOverlay" off \
                 com.heroicgameslauncher.hgl:flatpak "Heroic Games Launcher" off \
                 io.github.ilya_zlobintsev.LACT:flatpak "LACT (overclocking and/or undervolting GPU)" off \
                 ludusavi:pkg "Ludusavi" off \
                 net.lutris.Lutris:flatpak "Lutris" off \
                 mangohud-git:other "MangoHud" off \
                 com.moonlight_stream.Moonlight:flatpak "Moonlight (game streaming client)" off \
                 nexusmods-app-bin:pkg "Nexus Mods" off \
                 nonsteamlaunchers:other "NonSteamLaunchers" off \
                 ngfn:other "NVIDIA GeForce Now" off \
                 com.obsproject.Studio:flatpak "Open Broadcaster Software (OBS) Studio." off \
                 opengamepadui:other "Open Gamepad UI" off \
                 io.github.berarma.Oversteer:flatpak "Oversteer" off \
                 one.playtron.gamelab:other "Playtron GameLAB" off \
                 polychromatic:pkg "Polychromatic (Razer accessories)" off \
                 org.prismlauncher.PrismLauncher:flatpak "Prism Launcher" off \
                 proton-ge:other "Proton GE" off \
                 com.github.Matoking.protontricks:other "Protontricks" off \
                 net.davidotek.pupgui2:flatpak "ProtonUp-Qt" off \
                 remoteplaywhatever:pkg "RemotePlayWhatever" off \
                 net.retrodeck.retrodeck:flatpak "RetroDECK" off \
                 steam:other "Steam" off \
                 steamtinkerlaunch-git:pkg "Steam Tinker Launch" off \
                 dev.lizardbyte.app.Sunshine:flatpak "Sunshine (game streaming server)" off \
                 umu-launcher:pkg "umu-launcher" off \
                 waydroid:other "Waydroid (Android gaming)" off \
                 xcloud:other "Xbox Cloud Gaming" off \
                 xbox-controller-drivers:other "Xbox controller drivers" off \
                 zerotier-one:pkg "ZeroTier One VPN (CLI)" off \
                 zerotier-gui-git:pkg "ZeroTier One VPN (GUI)" off)
    for gamepkg in ${gamepkgs}
        do kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for ${gamepkg} to be installed..." 2 | cut -d" " -f1)
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1

        if echo "${gamepkg}" | grep -P ":flatpak$"; then
            flatpak_install "$(echo "${gamepkg}" | cut -d: -f1)"
        fi

        if echo "${gamepkg}" | grep -P ":pkg$"; then
            aur_install "$(echo "${gamepkg}" | cut -d: -f1)"
        fi

        if echo "${gamepkg}" | grep -P "^deckyloader:other$"; then
            decky_loader_install
        fi

        if echo "${gamepkg}" | grep -P "^emudeck:other$"; then
            EMUDECK_GITHUB_URL="https://api.github.com/repos/EmuDeck/emudeck-electron/releases/latest"
            EMUDECK_URL="$(curl -s ${EMUDECK_GITHUB_URL} | grep -E 'browser_download_url.*AppImage' | cut -d '"' -f 4)"
            curl --location "${EMUDECK_URL}" --output /home/"${USER}"/Desktop/EmuDeck.AppImage
        fi

        if echo "${gamepkg}" | grep -P "^gamescope:other$"; then
            pacman_install gamescope
            aur_install gamescope-session-git gamescope-session-steam-git
        fi

        if echo "${gamepkg}" | grep -P "^mangohud-git:other$"; then
            aur_install mangohud-git lib32-mangohud-git
            flatpak_install runtime/org.freedesktop.Platform.VulkanLayer.MangoHud/x86_64/23.08
        fi

        if echo "${gamepkg}" | grep -P "^nonsteamlaunchers:other$"; then
            curl --location --remote-name "https://raw.githubusercontent.com/moraroy/NonSteamLaunchers-On-Steam-Deck/refs/heads/main/NonSteamLaunchers.desktop" --output-dir /home/"${USER}"/Desktop/
        fi

        if echo "${gamepkg}" | grep -P "^ngfn:other$"; then
            chrome_install
            ln -s /home/"${USER}"/.winesapos/winesapos-ngfn.desktop /home/"${USER}"/Desktop/winesapos-ngfn.desktop
        fi

        if echo "${gamepkg}" | grep -P "^opengamepadui:other$"; then
            aur_install opengamepadui-bin opengamepadui-session-git
        fi

        if echo "${gamepkg}" | grep -P "^one.playtron.gamelab:other$"; then
            curl --location "https://api.playtron.one/api/v1/gamelab/download/linux_x64_appimage/latest" --output /home/"${USER}"/Desktop/GameLAB.AppImage
        fi

        if echo "${gamepkg}" | grep -P "^proton-ge:other$"; then
            proton_ge_install
        fi

        if echo "${gamepkg}" | grep -P "^com.github.Matoking.protontricks:other$";  then
            flatpak_install com.github.Matoking.protontricks
            # Add a wrapper script so that the Flatpak can be used normally via the CLI.
            echo '#!/bin/bash
flatpak run com.github.Matoking.protontricks $@
' | sudo tee /usr/local/bin/protontricks
            sudo chmod +x /usr/local/bin/protontricks
        fi

        if echo "${gamepkg}" | grep -P "^steam:other$"; then
            pacman_install steam steam-native-runtime
            steam_bootstrap
        fi

        if echo "${gamepkg}" | grep -P "^waydroid:other$"; then
            waydroid_install
        fi

        if echo "${gamepkg}" | grep -P "^xcloud:other$"; then
            chrome_install
            ln -s /home/"${USER}"/.winesapos/winesapos-xcloud.desktop /home/"${USER}"/Desktop/winesapos-xcloud.desktop
        fi

        if echo "${gamepkg}" | grep -P "^xbox-controller-drivers:other$"; then
            chrome_install
            ln -s /home/"${USER}"/.winesapos/winesapos-xcloud.desktop /home/"${USER}"/Desktop/winesapos-xcloud.desktop
        fi
        "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
    done
}

user_password_auto() {
    # Disable debug logging as to not leak password in the log file.
    set +x
    winesap_password=$(kdialog --title "winesapOS First-Time Setup" --password "Enter your new user password:")
    echo "${USER}:${winesap_password}" | sudo chpasswd
    # Re-enable debug logging.
    set -x
}

user_password_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to change your password?"; then
        user_password_auto
    fi
}

root_password_auto() {
    set +x
    root_password=$(kdialog --title "winesapOS First-Time Setup" --password "Enter the new root password:")
    echo "root:${root_password}" | sudo chpasswd
    set -x
}

root_password_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to change the root password?"; then
        root_password_auto
    fi
}

luks_password_auto() {
    if [[ "${WINESAPOS_IMAGE_TYPE}" == "secure" ]]; then
        # Example output: "mmcblk0p5", "nvme0n1p5", "sda5"
        root_partition_shortname=$(lsblk -o name,label | grep winesapos-luks | awk '{print $1}' | grep -o -P '[a-z]+.*')
        set +x
        luks_password=$(kdialog --title "winesapOS First-Time Setup" --password "Enter the new LUKS storage encryption password:")
        echo -e "password\n${luks_password}\n${luks_password}\n" | sudo cryptsetup luksChangeKey /dev/"${root_partition_shortname}"
        set -x
    fi
}

luks_password_ask() {
    if [[ "${WINESAPOS_IMAGE_TYPE}" == "secure" ]]; then
        if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to change the LUKS storage encryption password?"; then
            luks_password_auto
        fi
    fi
}

passwordless_login_remove() {
        for i in kde sddm; do
            sudo mv /etc/pam.d/"${i}" /etc/pam.d/"${i}"BAK
            grep -v "nopasswdlogin" /etc/pam.d/"${i}"BAK | sudo tee /etc/pam.d/"${i}"
            sudo rm -f /etc/pam.d/"${i}"BAK
        done
        sudo gpasswd --delete "${USER}" nopasswdlogin
        sudo groupdel nopasswdlogin
}

passwordless_login_auto() {
    if [[ "${WINESAPOS_IMAGE_TYPE}" == "secure" ]]; then
        passwordless_login_remove
    fi
}

passwordless_login_ask() {
    if ! kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to keep passwordless login enabled?"; then
        passwordless_login_remove
    fi
}

grub_hide_auto() {
    sudo crudini --ini-options=nospace --set /etc/default/grub "" GRUB_TIMEOUT 0
    sudo crudini --ini-options=nospace --set /etc/default/grub "" GRUB_TIMEOUT_STYLE hidden
}

grub_hide_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to hide the GRUB boot menu?"; then
        grub_hide_auto
    else
        sudo crudini --ini-options=nospace --set /etc/default/grub "" GRUB_TIMEOUT 10
        sudo crudini --ini-options=nospace --set /etc/default/grub "" GRUB_TIMEOUT_STYLE menu
    fi
}

firmware_upgrade_auto() {
    kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for hardware firmware to be upgraded..." 2 | cut -d" " -f1)
    sudo fwupdmgr refresh --force
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
    sudo fwupdmgr update --assume-yes --no-reboot-check
    "${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close
}

firmware_upgrade_ask() {
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to upgrade hardware firmware from LVFS with fwupdmgr?"; then
        firmware_upgrade_auto
    fi
}

winesapos_recommended_defaults=1
export winesapos_recommended_defaults
if [[ "${WINESAPOS_SETUP_INTERACTIVE}" == "true" ]]; then
    if kdialog --title "winesapOS First-Time Setup" --yesno "Do you want to use the recommended defaults for the first-time setup?"; then
        broadcom_wifi_auto
        loop_test_internet_connection
        winesapos_version_check
        repo_mirrors_region_auto
        steam_bootstrap
        screen_rotate_auto
        asus_setup
        framework_setup
        mac_setup
        msi_setup
        steam_deck_setup
        surface_setup
        graphics_drivers_auto
        swap_method_auto
        # There is currently no way to auto detect the locale so skip it for now.
        time_auto
        productivity_auto
        gaming_auto
        luks_password_auto
        passwordless_login_auto
        grub_hide_auto
        firmware_upgrade_auto
        user_password_auto
        root_password_auto
        locale_ask
    else
        winesapos_recommended_defaults=0
        broadcom_wifi_ask
        loop_test_internet_connection
        winesapos_version_check
        repo_mirrors_region_ask
        steam_bootstrap
        screen_rotate_ask
        asus_setup
        framework_setup
        mac_setup
        msi_setup
        steam_deck_setup
        surface_setup
        graphics_drivers_ask
        swap_method_ask
        time_ask
        desktops_ask
        productivity_ask
        gaming_ask
        btrfs_backups_ask
        luks_password_ask
        passwordless_login_ask
        grub_hide_ask
        firmware_upgrade_ask
        user_password_ask
        root_password_ask
        locale_ask
    fi
else
    broadcom_wifi_auto
    loop_test_internet_connection
    repo_mirrors_region_auto
    steam_bootstrap
    screen_rotate_auto
    asus_setup
    framework_setup
    mac_setup
    msi_setup
    steam_deck_setup
    surface_setup
    graphics_drivers_auto
    swap_method_auto
    time_auto
    productivity_auto
    gaming_auto
    luks_password_auto
    passwordless_login_auto
    grub_hide_auto
    firmware_upgrade_auto
fi

# Fix permissions.
chmod +x /home/"${USER}"/Desktop/*.AppImage
sudo chown 1000:1000 /home/"${USER}"/Desktop/*.desktop
chmod +x /home/"${USER}"/Desktop/*.desktop

# Remove the Flatpak directory for the user to avoid errors.
# This directory will automatically get re-generated when a 'flatpak' command is ran.
# https://github.com/winesapOS/winesapOS/issues/516
rm -r -f /home/"${USER}"/.local/share/flatpak

kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the new drivers to be enabled on boot..." 2 | cut -d" " -f1)
"${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1
# Regenerate the initramfs to load all of the new drivers.
sudo mkinitcpio -P
"${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close

# Regenerate the GRUB configuration to load the new Btrfs snapshots.
# This allows users to easily revert back to a fresh installation of winesapOS.
sudo grub-mkconfig -o /boot/grub/grub.cfg

# Delete the shortcut symlink so this will not auto-start again during the next login.
rm -f ~/.config/autostart/winesapos-setup.desktop

echo "Running first-time setup tests..."
kdialog_dbus=$(kdialog --title "winesapOS First-Time Setup" --progressbar "Please wait for the first-time setup tests to finish..." 2 | cut -d" " -f1)
failed_tests=0
winesapos_test_failure() {
    failed_tests=$((failed_tests + 1))
    printf "FAIL\n"
}

if [[ "${winesapos_recommended_defaults}" == "0" ]]; then
    printf "\tChecking that Btrfs quotas are enabled..."
    # There should be two entries for 50 GiB. One for root and one for home.
    if [[ "$(sudo btrfs qgroup show -pcre / | grep -c "${backup_size_selected}.00GiB")" == "2" ]]; then
        printf "PASS\n"
    else
        winesapos_test_failure
    fi
fi
"${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog Set org.kde.kdialog.ProgressDialog value 1

if [[ "${answer_install_ge}" == "true" ]]; then
    echo "Testing that GE Proton has been installed..."
    printf "\tChecking that GE Proton is installed..."
    # shellcheck disable=SC2010
    if ls -1 /home/"${USER}"/.local/share/Steam/compatibilitytools.d/ | grep -v -P ".tar.gz$" | grep -q -P "^GE-Proton.*"; then
        printf "PASS\n"
    else
        winesapos_test_failure
    fi

    printf "\tChecking that the GE Proton tarball has been removed..."
    # shellcheck disable=SC2010
    if ! ls -1 /home/"${USER}"/.local/share/Steam/compatibilitytools.d/ | grep -q -P ".tar.gz$"; then
        printf "PASS\n"
    else
        winesapos_test_failure
    fi
    echo "Testing that GE Proton has been installed complete."
fi

if sudo dmidecode -s system-product-name | grep -P "^(Galileo|Jupiter)"; then
    printf "\tChecking that GRUB enables S3 deep sleep support..."
    if sudo grep -q "mem_sleep_default=deep" /boot/grub/grub.cfg; then
        printf "PASS\n"
    else
        winesapos_test_failure
    fi
fi

export packages_missing=0
echo "Checking to see if there are any missing packages from Pacman..."
for i in "${packages_pacman[@]}"; do
    printf "%s..." "${i}"
    if /usr/bin/pacman -Q "${i}" &> /dev/null; then
        printf "PASS\n"
    else
        packages_missing=1
        winesapos_test_failure
    fi
done

echo "Checking to see if there are any missing packages from Flatpak..."
for i in "${packages_flatpak[@]}"; do
    printf "%s..." "${i}"
    if flatpak info "${i}" &> /dev/null; then
        printf "PASS\n"
    else
        packages_missing=1
        winesapos_test_failure
    fi
done
echo "Checking to see if there are any missing packages from Flatpak done."

echo "Number of failed tests: ${failed_tests}"
echo "Running first-time setup tests complete."
"${qdbus_cmd}" "${kdialog_dbus}" /ProgressDialog org.kde.kdialog.ProgressDialog.close

if [[ "${WINESAPOS_IMAGE_TYPE}" == "secure" ]]; then
    echo "Disallow passwordless 'sudo' now that the setup is done..."
    sudo -E sh -c 'rm -f /etc/sudoers.d/${WINESAPOS_USER_NAME}; mv /root/etc-sudoersd-${WINESAPOS_USER_NAME} /etc/sudoers.d/${WINESAPOS_USER_NAME}'
    echo "Disallow passwordless 'sudo' now that the setup is done complete."
fi

kdialog --title "winesapOS First-Time Setup" --msgbox "Please reboot to load new changes."
echo "End time: $(date --iso-8601=seconds)"
exit "${failed_tests}"
